package org.mm.web.controller;

import static org.mm.parameter.Parameter.PATIENT_MEASUREMENT_INSERT_RESPONSE;
import static org.mm.parameter.Parameter.SENSOR_LIST_DONE_RESPONSE;
import static org.mm.parameter.Parameter.SENSOR_MEASUREMENT_INSERT_RESPONSE;
import static org.mm.parameter.Parameter.THERAPY_LIST_RESPONSE;
import static org.mm.parameter.Parameter.THERAPY_READ_RESPONSE;
import static org.mm.parameter.Parameter.THERAPY_TASK_EVENT_UPDATE_RESPONSE;
import static org.mm.parameter.Parameter.URL_DATE_PATTERN;
import static org.mm.parameter.enm.Error.GENERIC;
import static org.mm.parameter.enm.Error.LOGIN_FAILED;
import static org.mm.parameter.enm.Error.PATIENT_ROLE;
import static org.mm.parameter.enm.Error.SENSOR_NOT_PRESENT;

import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.apache.commons.lang3.time.DateUtils;
import org.mm.exception.AppException;
import org.mm.mapper.Mapper;
import org.mm.model.Measurement;
import org.mm.model.Patient;
import org.mm.model.Sensor;
import org.mm.model.Therapy;
import org.mm.model.TherapyTask;
import org.mm.model.TherapyTaskEvent;
import org.mm.model.User;
import org.mm.service.MeasurementService;
import org.mm.service.PatientService;
import org.mm.service.SensorService;
import org.mm.service.TherapyService;
import org.mm.service.TherapyTaskEventService;
import org.mm.service.TherapyTaskService;
import org.mm.service.UserService;
import org.mm.xml.MeasurementResponseXML;
import org.mm.xml.ResponseXML;
import org.mm.xml.SensorResponseXML;
import org.mm.xml.TherapyResponseXML;
import org.mm.xml.entities.ErrorXML;
import org.mm.xml.entities.PatientXML;
import org.mm.xml.entities.SensorXML;
import org.mm.xml.entities.TherapyXML;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

@RequestMapping("/mdmt")
@Controller
public class MedicalMonitorController {

	private final Logger log = LoggerFactory.getLogger(MedicalMonitorController.class);

	@Autowired
	private UserService userService;

	@Autowired
	private PatientService patientService;

	@Autowired
	private TherapyService therapyService;

	@Autowired
	private TherapyTaskService therapyTaskService;

	@Autowired
	private TherapyTaskEventService therapyTaskEventService;

	@Autowired
	private SensorService sensorService;

	@Autowired
	private MeasurementService measurementService;

	/**
	 * retrieveTherapies
	 * @param userName
	 * @param userPassword
	 * @return therapyResponseXML
	 */
	@RequestMapping(value = "/therapy/list", method = RequestMethod.GET, produces = "application/xml")
	public @ResponseBody TherapyResponseXML retrieveTherapies(
			@RequestParam(value = "u", required = true) String userName,
			@RequestParam(value = "p", required = true) String userPassword
			) {
		TherapyResponseXML response = new TherapyResponseXML();
		response.setType(THERAPY_LIST_RESPONSE);
		try {
			User user = userService.findByUserNameAndUserPassword(userName, userPassword);
			if (user != null) {
				PatientXML patientXML = new PatientXML();
				Mapper.mapUserToPatientXML(user, patientXML);
				if (user.getRole().isPatient()) {
					Patient patient = patientService.findByParentUserId(user.getUserId());
					Mapper.mapPatientToPatientXML(patient, patientXML);
					response.setPatient(patientXML);
					List<Therapy> therapies = therapyService.findTherapyByPatient(patient);
					if (therapies != null) {
						List<TherapyXML> therapiesXML = new ArrayList<TherapyXML>();
						for (Therapy therapy : therapies) {
							TherapyXML therapyXML = new TherapyXML();
							Mapper.mapTherapyToTherapyXML(therapy, therapyXML);
							therapiesXML.add(therapyXML);
						}
						response.setTherapies(therapiesXML);
					}
				} else {

					throw new AppException(PATIENT_ROLE);
				}

			} else {
				//response.setError(new ErrorXML(LOGIN_FAILED.code, LOGIN_FAILED.message));
				throw new AppException(LOGIN_FAILED);
			}

		} catch (Exception e) {
			e.printStackTrace();
			log.error("retrieveTherapies", e);
			AppException xmle = (e instanceof AppException) ? (AppException) e : new AppException();
			response.setError(xmle.getError());
		}
		return response;
	}

	/**
	 * retrieveTherapy
	 * @param userName
	 * @param userPassword
	 * @param therapyId
	 * @return therapyResponseXML
	 */
	@RequestMapping(value = "/therapy/read", method = RequestMethod.GET, produces = "application/xml")
	public @ResponseBody TherapyResponseXML retrieveTherapy(
			@RequestParam(value = "u", required = true) String userName,
			@RequestParam(value = "p", required = true) String userPassword,
			@RequestParam(value = "tid", required = true) Long therapyId
			) {
		TherapyResponseXML response = new TherapyResponseXML();
		response.setType(THERAPY_READ_RESPONSE);
		try {
			User user = userService.findByUserNameAndUserPassword(userName, userPassword);
			if (user != null) {
				PatientXML patientXML = new PatientXML();
				Mapper.mapUserToPatientXML(user, patientXML);
				if (user.getRole().isPatient()) {
					Patient patient = patientService.findByParentUserId(user.getUserId());
					Mapper.mapPatientToPatientXML(patient, patientXML);
					response.setPatient(patientXML);
					Therapy therapy = therapyService.findTherapyByIdAndPatient(therapyId, patient);
					if (therapy != null) {
						TherapyXML therapyXML = new TherapyXML();
						Mapper.mapTherapyToTherapyXML(therapy, therapyXML);
						List<TherapyXML> therapiesXML = new ArrayList<TherapyXML>();
						therapiesXML.add(therapyXML);
						response.setTherapies(therapiesXML);
					}
				} else {
					new AppException(PATIENT_ROLE);
				}
			} else {
				//response.setError(new ErrorXML(LOGIN_FAILED.code, LOGIN_FAILED.message));
				new AppException(LOGIN_FAILED);
			}
		} catch (Exception e) {
			e.printStackTrace();
			log.error("retrieveTherapy", e);
			AppException xmle = (e instanceof AppException) ? (AppException) e : new AppException();
			response.setError(xmle.getError());
		}
		return response;
	}

	/**
	 * updateTherapyEvent
	 * @param userName
	 * @param userPassword
	 * @param therapyTaskId
	 * @param strScheduledDate
	 * @param strExecutionDate
	 * @return therapyResponseXML
	 */
	@RequestMapping(value = "/therapy/update", method = RequestMethod.GET, produces = "application/xml")
	public @ResponseBody ResponseXML updateTherapyEvent(
			@RequestParam(value = "u", required = true) String userName,
			@RequestParam(value = "p", required = true) String userPassword,
			@RequestParam(value = "tkid", required = true) Long therapyTaskId,
			@RequestParam(value = "tkst", required = true) String strScheduledDate,
			@RequestParam(value = "tket", required = true) String  strExecutionDate,
			@RequestParam(value = "sid", required = false) Long sensorId,
			@RequestParam(value = "md", required = false) String measurementData,
			@RequestParam(value = "mn", required = false) String measurementDescription
			) {
		TherapyResponseXML response = new TherapyResponseXML();
		response.setType(THERAPY_TASK_EVENT_UPDATE_RESPONSE);
		try {
			User user = userService.findByUserNameAndUserPassword(userName, userPassword);
			if (user != null) {
				PatientXML patientXML = new PatientXML();
				Mapper.mapUserToPatientXML(user, patientXML);
				if (user.getRole().isPatient()) {
					Timestamp now = new Timestamp(System.currentTimeMillis());
					Patient patient = patientService.findByParentUserId(user.getUserId());
					Mapper.mapPatientToPatientXML(patient, patientXML);
					response.setPatient(patientXML);
					Date scheduledDate = DateUtils.parseDate(strScheduledDate, URL_DATE_PATTERN);
					Date executionDate = DateUtils.parseDate(strExecutionDate, URL_DATE_PATTERN);
					TherapyTaskEvent therapyTaskEvent = therapyTaskEventService.findByTherapyTask(therapyTaskId, scheduledDate);

					TherapyTask therapyTask = therapyTaskEvent.getTherapyTask();
					checkRightSensor(therapyTask, sensorId);

					therapyTask.setTherapyTaskEvents(new ArrayList<TherapyTaskEvent>());
					therapyTask.addTherapyTaskEvent(therapyTaskEvent);

					Therapy therapy = therapyTask.getTherapy();
					therapy.setTherapyTasks(new ArrayList<TherapyTask>());
					therapy.addTherapyTask(therapyTask);

					therapyTaskEvent.setTherapyTaskEventScheduledDate(new Timestamp(scheduledDate.getTime()));
					therapyTaskEvent.setTherapyTaskEventExecutionDate(new Timestamp(executionDate.getTime()));

					therapyTaskEvent.setUser2(user);
					therapyTaskEvent.setTherapyTaskEventLastModificationDate(now);

					Measurement measurement = createMeasurement(
								user,
								(sensorId != null) ? therapyTask.getSensor() : null,
								patient,
								measurementDescription,
								measurementData,
								now);
					therapyTaskEvent.setMeasurement(measurement);

					therapyTaskEventService.save(therapyTaskEvent);

					TherapyXML therapyXML = new TherapyXML();
					Mapper.mapTherapyToTherapyXML(therapy, therapyXML, false);
					response.addTherapy(therapyXML);
				} else {
					new AppException(PATIENT_ROLE);
				}
			} else {
					new AppException(LOGIN_FAILED);
			}
		} catch (Exception e) {
			e.printStackTrace();
			log.error("updateTherapyEvent", e);
			AppException xmle = (e instanceof AppException) ? (AppException) e : new AppException();
			response.setError(xmle.getError());
		}
		return response;
	}

	@RequestMapping(value = "/sensor/list", method = RequestMethod.GET, produces = "application/xml")
	public @ResponseBody ResponseXML retrieveSensors(
			@RequestParam(value = "u", required = true) String userName,
			@RequestParam(value = "p", required = true) String userPassword
			) {
		SensorResponseXML response = new SensorResponseXML();
		response.setType(SENSOR_LIST_DONE_RESPONSE);
		try {
			User user = userService.findByUserNameAndUserPassword(userName, userPassword);
			if (user != null) {
				PatientXML patientXML = new PatientXML();
				Mapper.mapUserToPatientXML(user, patientXML);
				response.setPatient(patientXML);
				if (user.getRole().isPatient()) {
					Patient patient = patientService.findByParentUserId(user.getUserId());
					List<Sensor> sensors = sensorService.findByPatient(patient);
					Mapper.mapPatientToPatientXML(patient, patientXML);
					response.setPatient(patientXML);
					if (sensors != null) {
						List<SensorXML> sensorsXML = new ArrayList<SensorXML>();
						for (Sensor sensor : sensors) {
							SensorXML sensorXML = new SensorXML();
							Mapper.mapSensorToSensorXML(sensor, sensorXML);
							sensorsXML.add(sensorXML);
						}
						response.setSensors(sensorsXML);
					}
				} else {
					response.setError(new ErrorXML(PATIENT_ROLE.code, PATIENT_ROLE.message));
				}
			} else {
				response.setError(new ErrorXML(LOGIN_FAILED.code, LOGIN_FAILED.message));
			}
		} catch (Exception e) {
			e.printStackTrace();
			log.error("doTherapyTaskDone", e);
			ErrorXML error = new ErrorXML(GENERIC.code, GENERIC.message);
			response.setError(error);
		}
		return response;
	}

	@RequestMapping(value = "/measure/sensor", method = RequestMethod.GET, produces = "application/xml")
	public @ResponseBody ResponseXML measureSensor(
		@RequestParam(value = "u", required = true) String userName,
		@RequestParam(value = "p", required = true) String userPassword,
		@RequestParam(value = "sid", required = true) Long sensorId,
		@RequestParam(value = "md", required = true) String measurementData,
		@RequestParam(value = "mn", required = false) String measurementDescription
		) {
		MeasurementResponseXML response = new MeasurementResponseXML();
		response.setType(SENSOR_MEASUREMENT_INSERT_RESPONSE);
		try {
			User user = userService.findByUserNameAndUserPassword(userName, userPassword);
			if (user != null) {
				PatientXML patientXML = new PatientXML();
				Mapper.mapUserToPatientXML(user, patientXML);
				if (user.getRole().isPatient()) {
					Patient patient = patientService.findByParentUserId(user.getUserId());
					Mapper.mapPatientToPatientXML(patient, patientXML);
					response.setPatient(patientXML);
					Sensor sensor = sensorService.findById(sensorId);
					response.setSensor(Mapper.mapSensorToSensorXML(sensor));
					Measurement measurement = createMeasurement(
							user,
							sensor,
							patient,
							measurementDescription,
							measurementData,
							new Timestamp(System.currentTimeMillis()));
					response.setMeasurement(Mapper.mapMeasurementToMeasurementXML(measurement));
				} else {
					response.setError(new ErrorXML(PATIENT_ROLE.code, PATIENT_ROLE.message));
				}
			} else {
				response.setError(new ErrorXML(LOGIN_FAILED.code, LOGIN_FAILED.message));
			}

		} catch (Exception e) {
			e.printStackTrace();
			log.error("measureSensor", e);
			AppException xmle = (e instanceof AppException) ? (AppException) e : new AppException();
			response.setError(xmle.getError());
		}
		return response;
	}

	@RequestMapping(value = "/measure/patient", method = RequestMethod.GET, produces = "application/xml")
	public @ResponseBody ResponseXML measurePatient(
		@RequestParam(value = "u", required = true) String userName,
		@RequestParam(value = "p", required = true) String userPassword,
		@RequestParam(value = "md", required = true) String measurementData,
		@RequestParam(value = "mn", required = false) String measurementDescription
		) {
		MeasurementResponseXML response = new MeasurementResponseXML();
		response.setType(PATIENT_MEASUREMENT_INSERT_RESPONSE);
		try {
			User user = userService.findByUserNameAndUserPassword(userName, userPassword);
			if (user != null) {
				PatientXML patientXML = new PatientXML();
				Mapper.mapUserToPatientXML(user, patientXML);
				if (user.getRole().isPatient()) {
					Patient patient = patientService.findByParentUserId(user.getUserId());
					Mapper.mapPatientToPatientXML(patient, patientXML);
					response.setPatient(patientXML);
					Measurement measurement = createMeasurement(
							user,
							null,
							patient,
							measurementDescription,
							measurementData,
							new Timestamp(System.currentTimeMillis()));
					response.setMeasurement(Mapper.mapMeasurementToMeasurementXML(measurement));
				} else {
					response.setError(new ErrorXML(PATIENT_ROLE.code, PATIENT_ROLE.message));
				}
			} else {
				response.setError(new ErrorXML(LOGIN_FAILED.code, LOGIN_FAILED.message));
			}

		} catch (Exception e) {
			e.printStackTrace();
			log.error("measurePatient", e);
			AppException xmle = (e instanceof AppException) ? (AppException) e : new AppException();
			response.setError(xmle.getError());
		}
		return response;
	}

	/**
	 * Controllo per verificare se il sensorId in input è uguale
	 * a quello associato alla therapyTask determinata
	 */
	private void checkRightSensor(TherapyTask therapyTask, Long sensorId) throws AppException {
		if (sensorId != null) {
			Long therapyTaskSensorId = (therapyTask.getSensor() != null) ? therapyTask.getSensor().getSensorId() : null;
			if (therapyTaskSensorId == null || therapyTaskSensorId.longValue() != sensorId.longValue()) {
				throw new AppException(SENSOR_NOT_PRESENT);
			}
		}
	}

	private Measurement createMeasurement(User user, Sensor sensor, Patient patient,
			String measurementDescription, String measurementData, Timestamp now) throws Exception {
		Measurement measurement = null;
		if (measurementData != null) {
			measurement = new Measurement();
			measurement.setSensor(sensor);
			measurement.setPatient(patient);
			measurement.setMeasurementDescription(measurementDescription);
			measurement.setMeasurementData(measurementData);
			measurement.setMeasurementCreationDate(now);
			measurement.setMeasurementLastModificationDate(now);
			measurement.setUser1(user);
			measurement.setUser2(user);
			measurement = measurementService.save(measurement);
		}
		return measurement;
	}

}
